#include "systemc.h"

#include "cache.h"
#include "cache_controller.h"
#include "control_unit.h"
#include "ram.h"
#include "victim_cache.h"

int sc_main(int argc, char *argv[])
{

  /* using consructor:
   * sc_clock(sc_module_name, double, sc_time_unit, double, double sc_time_unit, bool)
   */
  sc_clock               clk("CLK", 10, SC_NS, 0.5,
                             0, SC_NS,
                             false);
  sc_signal< sc_logic >  rst;

  /* MEM HIERARCHY */
  /* CACHE CONTROLLER */
  sc_signal< sc_logic >  cc_rdy;
  sc_signal< sc_lv<32> > cc_dout;
  sc_signal< sc_logic >  cc_rw;
  sc_signal< sc_logic >  cc_cs;
  sc_signal< sc_lv<32> > cc_addr;
  sc_signal< sc_lv<32> > cc_din;
  /* RAM */
  sc_signal< sc_logic  > m_rdy;
  sc_signal< sc_lv<32> > m_dout0;
  sc_signal< sc_lv<32> > m_dout1;
  sc_signal< sc_lv<32> > m_dout2;
  sc_signal< sc_lv<32> > m_dout3;
  sc_signal< sc_logic >  m_cs;
  sc_signal< sc_logic >  m_rw;
  sc_signal< sc_lv<32> > m_addr;
  sc_signal< sc_lv<32> > m_din0;
  sc_signal< sc_lv<32> > m_din1;
  sc_signal< sc_lv<32> > m_din2;
  sc_signal< sc_lv<32> > m_din3;
  /* VICTIM CACHE*/
  sc_signal< sc_logic >  v_rdy;
  sc_signal< sc_logic >  v_hit;
  sc_signal< sc_lv<30> > v_tout;
  sc_signal< sc_lv<32> > v_dout0;
  sc_signal< sc_lv<32> > v_dout1;
  sc_signal< sc_lv<32> > v_dout2;
  sc_signal< sc_lv<32> > v_dout3;
  sc_signal< sc_logic >  v_cs;
  sc_signal< sc_logic >  v_rw;
  sc_signal< sc_lv<32> > v_addr;
  sc_signal< sc_lv<32> > v_din0;
  sc_signal< sc_lv<32> > v_din1;
  sc_signal< sc_lv<32> > v_din2;
  sc_signal< sc_lv<32> > v_din3;
  sc_signal< sc_logic >  v_set_v;   
  sc_signal< sc_logic >  v_set_d;
  /* CACHE */
  sc_signal< sc_logic >  c_rdy;
  sc_signal< sc_logic >  c_hit;
  sc_signal< sc_lv<25> > c_tout;
  sc_signal< sc_lv<32> > c_dout0;
  sc_signal< sc_lv<32> > c_dout1;
  sc_signal< sc_lv<32> > c_dout2;
  sc_signal< sc_lv<32> > c_dout3;
  sc_signal< sc_logic >  c_cs;
  sc_signal< sc_logic >  c_rw;
  sc_signal< sc_lv<32> > c_addr;
  sc_signal< sc_lv<32> > c_din0;
  sc_signal< sc_lv<32> > c_din1;
  sc_signal< sc_lv<32> > c_din2;
  sc_signal< sc_lv<32> > c_din3;
  sc_signal< sc_logic >  c_set_v;   
  sc_signal< sc_logic >  c_din_src;
  sc_signal< sc_logic >  c_block;
  /* CACHE CONTROLLER STATE */
  sc_signal< sc_lv<3> >  cc_state;

  /* CPU REGS */
  sc_signal< sc_lv<32> > pc;
  sc_signal< sc_lv<32> > ir;
  sc_signal< sc_lv<32> > r0;
  sc_signal< sc_lv<32> > r1;
  sc_signal< sc_lv<32> > r2;
  sc_signal< sc_lv<32> > r3;
  sc_signal< sc_lv<32> > r4;
  sc_signal< sc_lv<32> > r5;
  sc_signal< sc_lv<32> > md;
  sc_signal< sc_lv<32> > a;
  sc_signal< sc_lv<32> > b;
  sc_signal< sc_lv<32> > alu_out;
  sc_signal< sc_lv<32> > target;

  /* CONTROL UNIT STATE */
  sc_signal< sc_lv<4> >  cu_state;


  /* MAPPINGS */
  /* CONTROL UNIT */
  control_unit cu("CONTROL_UNIT");
  cu.clk(clk);
  cu.rst(rst);
  cu.cc_rdy(cc_rdy);
  cu.cc_dout(cc_dout);
  cu.cc_rw(cc_rw);
  cu.cc_cs(cc_cs);
  cu.cc_addr(cc_addr);
  cu.cc_din(cc_din);
  cu.state(cu_state);
  /* CPU REGS */
  cu.pc(pc);
  cu.ir(ir);
  cu.r0(r0);
  cu.r1(r1);
  cu.r2(r2);
  cu.r3(r3);
  cu.r4(r4);
  cu.r5(r5);
  cu.md(md);
  cu.a(a);
  cu.b(b);
  cu.alu_out(alu_out);
  cu.target(target);

  /* CACHE CONTROLLER */
  cache_controller cc("CACHE_CONTROLLER");
  cc.cs(cc_cs);
  cc.rw(cc_rw);
  cc.addr(cc_addr);
  cc.din(cc_din);
  cc.rdy(cc_rdy);
  cc.dout(cc_dout);
  /* RAM */
  cc.m_rdy(m_rdy);
  cc.m_dout0(m_dout0);
  cc.m_dout1(m_dout1);
  cc.m_dout2(m_dout2);
  cc.m_dout3(m_dout3);
  cc.m_cs(m_cs);
  cc.m_rw(m_rw);
  cc.m_addr(m_addr);
  cc.m_din0(m_din0);
  cc.m_din1(m_din1);
  cc.m_din2(m_din2);
  cc.m_din3(m_din3);
  /* VICTIM CACHE */
  cc.v_rdy(v_rdy);
  cc.v_hit(v_hit);
  cc.v_tout(v_tout);
  cc.v_dout0(v_dout0);
  cc.v_dout1(v_dout1);
  cc.v_dout2(v_dout2);
  cc.v_dout3(v_dout3);
  cc.v_cs(v_cs);
  cc.v_rw(v_rw);
  cc.v_addr(v_addr);
  cc.v_din0(v_din0);
  cc.v_din1(v_din1);
  cc.v_din2(v_din2);
  cc.v_din3(v_din3);
  cc.v_set_v(v_set_v);   
  cc.v_set_d(v_set_d);
  /* CACHE */
  cc.c_rdy(c_rdy);
  cc.c_hit(c_hit);
  cc.c_tout(c_tout);
  cc.c_dout0(c_dout0);
  cc.c_dout1(c_dout1);
  cc.c_dout2(c_dout2);
  cc.c_dout3(c_dout3);
  cc.c_cs(c_cs);
  cc.c_rw(c_rw);
  cc.c_addr(c_addr);
  cc.c_din0(c_din0);
  cc.c_din1(c_din1);
  cc.c_din2(c_din2);
  cc.c_din3(c_din3);
  cc.c_set_v(c_set_v);   
  cc.c_din_src(c_din_src);
  cc.c_block(c_block);
  /* CACHE CONTROLLER STATE */
  cc.state(cc_state);

  /* CACHE */
  cache c("CACHE");
  c.cs(c_cs);
  c.rw(c_rw);
  c.addr(c_addr);
  c.din0(c_din0);
  c.din1(c_din1);
  c.din2(c_din2);
  c.din3(c_din3);
  c.set_v(c_set_v);   
  c.din_src(c_din_src);
  c.block(c_block);
  c.rdy(c_rdy);
  c.hit(c_hit);
  c.tout(c_tout);
  c.dout0(c_dout0);
  c.dout1(c_dout1);
  c.dout2(c_dout2);
  c.dout3(c_dout3);

  /* VICTIM CACHE */
  victim_cache v("VICTIM_CACHE");
  v.cs(v_cs);
  v.rw(v_rw);
  v.addr(v_addr);
  v.din0(v_din0);
  v.din1(v_din1);
  v.din2(v_din2);
  v.din3(v_din3);
  v.set_v(v_set_v);   
  v.set_d(v_set_d);
  v.rdy(v_rdy);
  v.hit(v_hit);
  v.tout(v_tout);
  v.dout0(v_dout0);
  v.dout1(v_dout1);
  v.dout2(v_dout2);
  v.dout3(v_dout3);

  /* RAM */
  ram m("RAM");
  m.cs(m_cs);
  m.rw(m_rw);
  m.addr(m_addr);
  m.din0(m_din0);
  m.din1(m_din1);
  m.din2(m_din2);
  m.din3(m_din3);
  m.rdy(m_rdy);
  m.dout0(m_dout0);
  m.dout1(m_dout1);
  m.dout2(m_dout2);
  m.dout3(m_dout3);


  /* MAPPINGS END*/

  sc_trace_file *f = sc_create_vcd_trace_file("c_wave");

  f->set_time_unit(100, SC_PS);

  sc_trace(f, cu.clk, "clk");
  sc_trace(f, cu.rst, "rst");
  sc_trace(f, cu.cc_rdy, "cc_rdy");
  sc_trace(f, cu.cc_dout, "cc_dout");
  sc_trace(f, cu.cc_rw, "cc_rw");
  sc_trace(f, cu.cc_cs, "cc_cs");
  sc_trace(f, cu.cc_addr, "cc_addr");
  sc_trace(f, cu.cc_din, "cc_din");
  sc_trace(f, cu.state, "cu_state");
  sc_trace(f, cu.pc, "pc");
  sc_trace(f, cu.ir, "ir");
  sc_trace(f, cu.r0, "r0");
  sc_trace(f, cu.r1, "r1");
  sc_trace(f, cu.r2, "r2");
  sc_trace(f, cu.r3, "r3");
  sc_trace(f, cu.r4, "r4");
  sc_trace(f, cu.r5, "r5");
  sc_trace(f, cu.md, "md");
  sc_trace(f, cu.a, "a");
  sc_trace(f, cu.b, "b");
  sc_trace(f, cu.alu_out, "alu_out");
  sc_trace(f, cu.target, "target");
  /* MEM HIERARCHY TRACING */
  sc_trace(f, cc.rdy, "cc_rdy");
  sc_trace(f, cc.dout, "cc_dout");
  sc_trace(f, cc.rw, "cc_rw");
  sc_trace(f, cc.cs, "cc_cs");
  sc_trace(f, cc.addr, "cc_addr");
  sc_trace(f, cc.din, "cc_din");
  sc_trace(f, cc.m_rdy, "m_rdy");
  sc_trace(f, cc.m_dout0, "m_dout0");
  sc_trace(f, cc.m_dout1, "m_dout1");
  sc_trace(f, cc.m_dout2, "m_dout2");
  sc_trace(f, cc.m_dout3, "m_dout3");
  sc_trace(f, cc.m_cs, "m_cs");
  sc_trace(f, cc.m_rw, "m_rw");
  sc_trace(f, cc.m_addr, "m_addr");
  sc_trace(f, cc.m_din0, "m_din0");
  sc_trace(f, cc.m_din1, "m_din1");
  sc_trace(f, cc.m_din2, "m_din2");
  sc_trace(f, cc.m_din3, "m_din3");
  sc_trace(f, cc.v_rdy, "v_rdy");
  sc_trace(f, cc.v_hit, "v_hit");
  sc_trace(f, cc.v_tout, "v_tout");
  sc_trace(f, cc.v_dout0, "v_dout0");
  sc_trace(f, cc.v_dout1, "v_dout1");
  sc_trace(f, cc.v_dout2, "v_dout2");
  sc_trace(f, cc.v_dout3, "v_dout3");
  sc_trace(f, cc.v_cs, "v_cs");
  sc_trace(f, cc.v_rw, "v_rw");
  sc_trace(f, cc.v_addr, "v_addr");
  sc_trace(f, cc.v_din0, "v_din0");
  sc_trace(f, cc.v_din1, "v_din1");
  sc_trace(f, cc.v_din2, "v_din2");
  sc_trace(f, cc.v_din3, "v_din3");
  sc_trace(f, cc.v_set_v, "v_set_v");   
  sc_trace(f, cc.v_set_d, "v_set_d");
  sc_trace(f, cc.c_rdy, "c_rdy");
  sc_trace(f, cc.c_hit, "c_hit");
  sc_trace(f, cc.c_tout, "c_tout");
  sc_trace(f, cc.c_dout0, "c_dout0");
  sc_trace(f, cc.c_dout1, "c_dout1");
  sc_trace(f, cc.c_dout2, "c_dout2");
  sc_trace(f, cc.c_dout3, "c_dout3");
  sc_trace(f, cc.c_cs, "c_cs");
  sc_trace(f, cc.c_rw, "c_rw");
  sc_trace(f, cc.c_addr, "c_addr");
  sc_trace(f, cc.c_din0, "c_din0");
  sc_trace(f, cc.c_din1, "c_din1");
  sc_trace(f, cc.c_din2, "c_din2");
  sc_trace(f, cc.c_din3, "c_din3");
  sc_trace(f, cc.c_set_v, "c_set_v");   
  sc_trace(f, cc.c_din_src, "c_din_src");
  sc_trace(f, cc.c_block, "c_block");
  sc_trace(f, cc.state, "cc_state");


  /* start sim */

  rst.write(SC_LOGIC_0);
  sc_start(10000, SC_NS);

  rst.write(SC_LOGIC_1);
  sc_start(1, SC_NS);

  rst.write(SC_LOGIC_0);
  sc_start(20, SC_NS);

  sc_start(10, SC_NS);

  sc_close_vcd_trace_file(f);
  return 0;
}
